using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac;
using Autofac.Builder;
using Autofac.Core;
using NSubstitute;
using uMVP;
using uMVP.AutofacExtends;
using UnityEngine;

namespace AutofacContrib.NSubstitute
{
    public class NSubstituteMvpRegistrationSource<TViewClass, TPresenterInterface> : IRegistrationSource
        where TViewClass : ViewBase
        where TPresenterInterface : class, IPresenter
    {
        private readonly object[] _parameters;

        public NSubstituteMvpRegistrationSource(params object[] parameters)
        {
            _parameters = parameters;
        }

        public IEnumerable<IComponentRegistration> RegistrationsFor(Service service,
            Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
        {
            if (service == null)
                throw new ArgumentNullException(nameof(service));

            var typedService = service as IServiceWithType;
            if (typedService == null || 
                !typeof(IView).IsAssignableFrom(typedService.ServiceType))
                return Enumerable.Empty<IComponentRegistration>();

            var rb = RegistrationBuilder.ForDelegate((c, p) =>
                {
                    var classType = typeof(TViewClass);
                    var constructors = classType.GetConstructors();
                    var constructor = constructors.First();
                    var parameters = constructor.GetParameters();

                    var parameterObjects = new List<object>(_parameters);
                    foreach (var parameter in parameters)
                    {
                        var obj = c.Resolve(parameter.ParameterType);
                        parameterObjects.Add(obj);
                    }

                    var view = Activator.CreateInstance(classType, parameterObjects.ToArray(), null);
                    CreateCanvas(view);
                    ContainerBuilderExtends.InitializePrefabInstance(view);
                    var presenter = Substitute.For<TPresenterInterface>();
                    ((IView<TPresenterInterface>)view).Initialize(presenter);
                    presenter.Initialize();
                    return view;
                })
                .As(service)
                .InstancePerLifetimeScope();

            return new[] { rb.CreateRegistration() };
        }

        private Canvas CreateCanvas(object view)
        {
            MemberInfo info = view.GetType();
            var attributes = info.GetCustomAttributes(typeof(PrefabPathAttribute), true);
            foreach (var attribute in attributes)
            {
                if (attribute.GetType() == typeof(PrefabPathAttribute))
                {
                    var prefabPathAttribute = (PrefabPathAttribute)attribute;
                    var path = prefabPathAttribute.ParentPath;
                    var canvasObject = GameObject.Find(path);
                    if (canvasObject != null)
                        UnityEngine.Object.Destroy(canvasObject);

                    return CreateCanvas(path);
                }
            }

            return null;
        }

        private Canvas CreateCanvas(string path)
        {
            var nameList = path.Split('/');

            GameObject go = null;
            foreach (var name in nameList)
            {
                if (string.IsNullOrEmpty(name))
                    continue;
                
                var gameObject = new GameObject(name);
                if (go != null)
                    gameObject.transform.SetParent(go.transform);
                go = gameObject;
            }
            
            return go.AddComponent<Canvas>();
        }

        public bool IsAdapterForIndividualComponents => false;
    }
}